package com.zyc.proxy;

import com.zyc.enums.RpcErrorMessageEnum;
import com.zyc.enums.RpcResponseCodeEnum;
import com.zyc.exception.RpcException;
import com.zyc.remoting.dto.RpcRequest;
import com.zyc.remoting.dto.RpcResponse;
import com.zyc.remoting.transport.RpcRequestTransport;
import com.zyc.remoting.transport.netty.client.NettyRpcClient;
import com.zyc.remoting.transport.socket.SocketRpcClient;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;

/**
 *
 * @author zyc
 * @create 2022--05--10 11:39
 */
@Slf4j
public class RpcClientProxy implements InvocationHandler {

    //基于动态代理发送请求给服务端，通信方式可以是socket或者其他
    private final RpcRequestTransport rpcRequestTransport;

    public RpcClientProxy(RpcRequestTransport rpcRequestTransport) {
        this.rpcRequestTransport = rpcRequestTransport;
    }

    /**
     * 获取代理对象
     * @param clazz 被代理对象的类
     * @param <T>
     * @return 代理对象
     */
    public <T> T getProxy(Class<T> clazz) {
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz}, this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("唤醒远程方法:[{}]", method.getName());
        RpcRequest rpcRequest = RpcRequest.builder().methodName(method.getName())
                .parameters(args)
                .paramTypes(method.getParameterTypes())
                .interfaceName(method.getDeclaringClass().getName())
                .requestId(UUID.randomUUID().toString())
                .build();

        RpcResponse<Object> rpcResponse = null;
        if (rpcRequestTransport instanceof SocketRpcClient) {
            log.info("代理socket通信");
            rpcResponse = rpcRequestTransport.sendRpcRequest(rpcRequest);
        }
        if (rpcRequestTransport instanceof NettyRpcClient) {
            log.info("代理netty通信");
            rpcResponse = rpcRequestTransport.sendRpcRequest(rpcRequest);
        }
        this.check(rpcResponse, rpcRequest);
        return rpcResponse.getData();
    }

    @SneakyThrows
    private void check(RpcResponse<Object> rpcResponse, RpcRequest rpcRequest) {
        if (rpcResponse == null) {
            throw new RpcException(RpcErrorMessageEnum.SERVICE_INVOCATION_FAILURE, "接口为：" + rpcRequest.getInterfaceName());
        }

        if (!rpcRequest.getRequestId().equals(rpcResponse.getRequestId())) {
            throw new RpcException(RpcErrorMessageEnum.REQUEST_NOT_MATCH_RESPONSE, "接口为：" + rpcRequest.getInterfaceName());
        }

        if (rpcResponse.getCode() == null || !rpcResponse.getCode().equals(RpcResponseCodeEnum.SUCCESS.getCode())) {
            throw new RpcException(RpcErrorMessageEnum.SERVICE_INVOCATION_FAILURE, "接口为：" + rpcRequest.getInterfaceName());
        }
    }
}
